package com.runwalk.video.blobs;

import java.lang.reflect.*;

//==================================================
//class BlobDetection
//==================================================
public class BlobDetection extends EdgeDetection
{
	// Temp
	Object	parent;
	Method 	filterBlobMethod;
	// Temp

	public static int        	blobMaxNumber = 1000;
	public int        			blobNumber;
	public Blob[]     			blob;
	public boolean[]  			gridVisited;

	public int					blobWidthMin, blobHeightMin;

	//	--------------------------------------------
	// Constructor
	//	--------------------------------------------
	public BlobDetection(int imgWidth, int imgHeight)
	{
		super(imgWidth, imgHeight);

		gridVisited   = new boolean[nbGridValue];
		blob         	= new Blob[blobMaxNumber];
		blobNumber   	= 0;
		for (int i=0 ; i<blobMaxNumber;i++) 
			blob[i] = new Blob(this); 

		blobWidthMin	= 0;
		blobHeightMin	= 0;

		filterBlobMethod = null;
	}

	//--------------------------------------------
	// setBlobDimensionMin()
	//--------------------------------------------
	/*
public void setBlobDimensionMin(int w, int h)
{
  if (w<0) w=0;
  if (h<0) h=0;
  if (w>imgWidth) w=imgWidth;
  if (h>imgHeight) h=imgHeight;

  blobWidthMin	= w;
  blobHeightMin	= h;
}
	 */
	//--------------------------------------------
	// setNumberBlobMax()
	//--------------------------------------------
	public void setBlobMaxNumber(int nb)
	{
		blobMaxNumber = nb;
	}

	//--------------------------------------------
	// getBlob()
	//--------------------------------------------
	public Blob getBlob(int n)
	{
		Blob b=null;
		if (n<blobNumber)
			return blob[n];
		return b;
	}

	//--------------------------------------------
	// getBlobNb()
	//--------------------------------------------
	public int getBlobNb()
	{
		return blobNumber;
	}

	//--------------------------------------------
	// computeBlobs()
	//--------------------------------------------
	public void computeBlobs(int[] pixels)
	{
		// Image
		setImage(pixels);

		// Clear gridVisited
		for (int i=0 ; i<nbGridValue; i++) 
			gridVisited[i]=false;

		// Compute Isovalue
		computeIsovalue();

		// Get Lines indices
		int		x,y,squareIndex;
		int		offset;
		float	vx,vy;

		nbLineToDraw 	= 0;
		vx	     		= 0.0f;
		blobNumber   	= 0;
		for (x=0 ; x<resx-1 ; x++)
		{
			vy = 0.0f;
			for (y=0 ; y<resy-1 ; y++)
			{
				// > offset in the grid
				offset		= x + resx*y;

				// > if we were already there, just go the next square!
				if (gridVisited[offset] == true) continue;

				// > squareIndex
				squareIndex     = getSquareIndex(x,y);

				// >Found something
				if (squareIndex > 0 && squareIndex < 15)
				{
					if (blobNumber < blobMaxNumber)
					{
						findBlob(blobNumber,x,y);
						blobNumber++;

					}
				}
				vy += stepy;
			}	// for y
			vx += stepx;
		}	// for x
		nbLineToDraw /= 2;
		//blobNumber+=1;
	}

	//--------------------------------------------
	// findBlob()
	//--------------------------------------------
	public void findBlob(int iBlob, int x, int y)
	{
		// Reset Blob values

		blob[iBlob].id     = iBlob; 
		blob[iBlob].xMin   =  1000.0f;
		blob[iBlob].xMax   =  -1000.0f;
		blob[iBlob].yMin   =  1000.0f;
		blob[iBlob].yMax   =  -1000.0f;
		blob[iBlob].nbLine = 0; 


		// Find it !!    
		computeEdgeVertex(iBlob, x,y);
		{

			// > This is just a temp patch (somtimes 'big' blobs are detected on the grid edges)

			if (blob[iBlob].xMin>=1000.0f || blob[iBlob].xMax<=-1000.0f || blob[iBlob].yMin>=1000.0f || blob[iBlob].yMax<=-1000.0f)
				blobNumber--;    
			else  
			{
				blob[iBlob].update();
				// User Filter
				if (filterBlobMethod != null)
				{
					try 
					{
						Boolean returnObj = (Boolean)(filterBlobMethod.invoke(parent, new Object[]{ blob[iBlob] } ));
						boolean returnValue = returnObj.booleanValue();
						if (returnValue == false) blobNumber--;    
					} 
					catch (Exception e) 
					{
						System.out.println("Disabling filterBlobMethod() because of an error.");
						filterBlobMethod = null;
					}
				}
			}

		}


	}

	//--------------------------------------------
	// computeEdgeVertex()
	//--------------------------------------------
	void computeEdgeVertex(int iBlob, int x, int y)
	{
		// offset
		int offset = x+resx*y;

		// Mark voxel as visited
		if (gridVisited[offset] == true) return;
		gridVisited[offset]=true;    

		// 
		int   iEdge, offx, offy, offAB;
		int[] edgeOffsetInfo;
		int   squareIndex     = getSquareIndex(x,y);
		float vx = (float)x*stepx;
		float vy = (float)y*stepy;

		int   n = 0;
		while ( (iEdge = MetaballsTable.edgeCut[squareIndex][n++]) != -1)
		{
			edgeOffsetInfo  = MetaballsTable.edgeOffsetInfo[iEdge];
			offx			= edgeOffsetInfo[0];
			offy			= edgeOffsetInfo[1];
			offAB			= edgeOffsetInfo[2];

			if (blob[iBlob].nbLine < Blob.MAX_NBLINE) 
				lineToDraw[nbLineToDraw++] = blob[iBlob].line[blob[iBlob].nbLine++] = voxel[(x+offx) + resx*(y+offy)] + offAB;
			else
				return;
		}

		int   toCompute = MetaballsTable.edgeToCompute[squareIndex];
		float t = 0.0f;
		float value = 0.0f;
		if (toCompute>0)
		{
			if ( (toCompute & 1) > 0) // Edge 0
			{
				t	= (isovalue - gridValue[offset]) / (gridValue[offset+1] - gridValue[offset]); 
				value   = vx*(1.0f-t) + t*(vx+stepx); 
				edgeVrt[voxel[offset]].x = value;

				if (value < blob[iBlob].xMin ) blob[iBlob].xMin = value;
				if (value > blob[iBlob].xMax ) blob[iBlob].xMax = value;

			}
			if ( (toCompute & 2) > 0) // Edge 3
			{
				t	= (isovalue - gridValue[offset]) / (gridValue[offset+resx] - gridValue[offset]); 
				value   = vy*(1.0f-t) + t*(vy+stepy);
				edgeVrt[voxel[offset]+1].y = value;

				if (value < blob[iBlob].yMin ) blob[iBlob].yMin = value;
				if (value > blob[iBlob].yMax ) blob[iBlob].yMax = value;

			}

		} // toCompute

		// Propagate to neightbors : use of Metaballs.neighborsTable 
		byte neighborVoxel = MetaballsTable.neightborVoxel[squareIndex];    
		if (x<resx-2   && (neighborVoxel & (1<<0))>0)        computeEdgeVertex(iBlob, x+1,y);
		if (x>0        && (neighborVoxel & (1<<1))>0)        computeEdgeVertex(iBlob, x-1,y);
		if (y<resy-2   && (neighborVoxel & (1<<2))>0)        computeEdgeVertex(iBlob, x,y+1);
		if (y>0        && (neighborVoxel & (1<<3))>0)        computeEdgeVertex(iBlob, x,y-1);

	}

	//--------------------------------------------
	// filterBlob()
	//--------------------------------------------
	/*
public boolean acceptBlob(Blob b)
{
	if ( (b.w*imgWidth>=blobWidthMin) || (b.h*imgHeight>=blobHeightMin) )
		return true;
	return false;
}
	 */
	//--------------------------------------------
	// activeCustomFilter
	//--------------------------------------------
	public void activeCustomFilter(Object parent)
	{
		this.parent = parent;
		try 
		{
			filterBlobMethod = parent.getClass().getMethod("newBlobDetectedEvent", new Class[] { Blob.class });
			//System.out.println("newBlobDetectedEvent found!");
		} catch (Exception e) 
		{
			System.out.println("no such metho or error");
			// no such method, or an error.. which is fine, just ignore
		}
	}

};
